package net.jxta.myjxta.plugins.diagnostic;

import net.jxta.endpoint.Message;
import net.jxta.endpoint.MessageElement;
import net.jxta.myjxta.dialog.Dialog;
import net.jxta.myjxta.dialog.DialogManager;
import net.jxta.myjxta.dialog.DialogMessage;
import net.jxta.myjxta.dialog.DialogPipeListener;
import net.jxta.myjxta.plugin.IPluginNotificationHandler;
import net.jxta.myjxta.plugin.ISelectableNode;
import net.jxta.myjxta.plugin.Plugin;
import net.jxta.myjxta.plugin.PluginContainer;
import net.jxta.myjxta.plugins.groupchat.ManyToManyDialog;
import net.jxta.myjxta.util.Group;
import net.jxta.myjxta.util.Logging;
import net.jxta.peergroup.PeerGroup;
import net.jxta.pipe.PipeService;
import net.jxta.util.JxtaBiDiPipe;

import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.logging.Logger;

/**
 * Plugin for GroupMessage Tracing/Debugging
 */
public final class GroupChatTracePlugin implements Plugin, IPluginNotificationHandler, PluginContainer.IPopupProvider, DialogPipeListener {

    private static final Logger LOG = Logger.getLogger(GroupChatTracePlugin.class.getName());

    private PluginContainer m_container;
    private boolean m_running = false;
    private DialogManager m_dialogManager;
    private final ArrayList<Group> m_joinedGroups = new ArrayList<Group>();


    public GroupChatTracePlugin() {
        LOG.log(java.util.logging.Level.FINE, "TracePlugin Constructor called");
    }

    public void init(PluginContainer c) {
        m_container = c;
        LOG.log(java.util.logging.Level.FINE, "TracePlugin init");
        ManyToManyDialog.ACTIVATE_SEQNR = true;
        //deactivated autostart
        //start(); // this is an autostart plugin
    }

    public void start() {
        LOG.log(Logging.STATUS_LEVEL, "TracePlugin started");
        m_container.registerPopupProvider(this);

        m_running = true;
        for (Group group : m_joinedGroups) {
            startListenerForGroup(group);
        }
    }

    public void stop() {
        m_container.removePopupProvider(this);
        LOG.log(Logging.STATUS_LEVEL, "TracePlugin stopped");
        for (Group group : m_joinedGroups) {
            stopListenerForGroup(group);
        }
        m_running = false;
    }

    public void destroy() {
        if (m_running) {
            stop();
        }
        LOG.log(java.util.logging.Level.FINE, "TracePlugin destroyed");
    }

    public String getName() {
        return "Groupchat Trace Plugin";
    }

    // END OF PLUGIN API

    // HELPER METHODS

    private void startListenerForGroup(Group p_group) {
        System.out.println("starting trace listener for group" + p_group);
        PeerGroup pg = p_group.getPeerGroup();
        m_dialogManager = getMany2ManyDialogManager(p_group, pg);
        m_dialogManager.addPipeListener(pg, this);
    }

    private void stopListenerForGroup(Group p_group) {
        System.out.println("stopping trace listener for group" + p_group);
        PeerGroup pg = p_group.getPeerGroup();
        m_dialogManager = getMany2ManyDialogManager(p_group, pg);
        m_dialogManager.removePipeListener(pg, this);
    }


    public boolean isRunning() {
        return m_running;
    }

    public IPluginNotificationHandler getPluginNotificationHander() {
        return this;  //for now we will implement this interface ourselve, it it will grow so we cant stay here...
    }

    public void groupJoined(Group p_group) {
        if (!p_group.isVisible())
            return;

        m_joinedGroups.add(p_group);

        if (isRunning()) {
            startListenerForGroup(p_group);
        }
    }

    public void groupResigned(Group p_group) {
        if (!p_group.isVisible())
            return;

        m_joinedGroups.remove(p_group);
        if (isRunning()) {
            stopListenerForGroup(p_group);
        }
    }

    private DialogManager getMany2ManyDialogManager(Group p_group, PeerGroup p_pg) {
        return DialogManager.getInstance(p_group,
                Dialog.getDialogNamer(ManyToManyDialog.class).
                        getDialogName(p_pg.getPeerName()),
                PipeService.PropagateType);
    }

    public void groupStateChanged(Group p_group) {
    }


    public void popupRequested(PluginContainer.IPopupGenerator popupGenerator, ISelectableNode[] selectedNodes, MouseEvent triggerEvent) {

    }


    public void receive(PeerGroup pg, Message msg) {
        DialogMessage dmsg = new DialogMessage(msg, ManyToManyDialog.KEYS);
        MessageElement messageElement = dmsg.getMessageElement(ManyToManyDialog.SEQ_NR_MSGKEY);

        if (messageElement != null) {

            Integer nr = new Integer(messageElement.toString());
            checkForMissedMessages(Group.getGroupNode(pg.getPeerGroupID()).getGroup(), dmsg.getOriginator(), nr);
        }
    }

    private final HashMap<GroupAuthorKey, Integer> m_memory = new HashMap<GroupAuthorKey, Integer>();

    private boolean checkForMissedMessages(Group group, String p_originator, Integer p_nr) {
        GroupAuthorKey key = new GroupAuthorKey(group, p_originator);
        Integer lastMsg = m_memory.get(key);
        String groupName = group.getName();
        m_memory.put(key, p_nr);
        if (lastMsg == null) {
            debug("received seq nr. " + p_nr + "(first msg) from " + p_originator + " in " + groupName);
            return false; //not known -> first message
        } else {
            int missed = p_nr.intValue() - (lastMsg.intValue() + 1);
            if (missed > 0) {
                warn(group, "WARNING - missed " + missed + " messages from " + p_originator + "@" + groupName + " (last seen nr:" + lastMsg + " received:" + p_nr + ")");
                return true;
            } else {
                debug("received seq nr. " + p_nr + " from " + p_originator);
                return false;
            }
        }
    }

    private void debug(String message) {
        if (isRunning()) {
            System.out.println(message);
        }
    }

    private void warn(Group group, String message) {
        if (isRunning()) {
//            DialogManager.getDialog(ManyToManyDialog.class,group, m_container.getMyJxta()).dispatch(message);
        }
        System.out.println(message);
    }

    public void receive(PeerGroup pg, JxtaBiDiPipe pipe) {
        // TODO Auto-generated method stub

    }


    //simple two value key for the hashmap
    private class GroupAuthorKey {
        private final Group group;
        private final String sender;

        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            final GroupAuthorKey that = (GroupAuthorKey) o;

            return !(group != null ? !group.equals(that.group) : that.group != null) && !(sender != null ? !sender.equals(that.sender) : that.sender != null);

        }

        public int hashCode() {
            int result;
            result = (group != null ? group.hashCode() : 0);
            result = 29 * result + (sender != null ? sender.hashCode() : 0);
            return result;
        }

        public GroupAuthorKey(Group p_group, String p_sender) {
            group = p_group;
            sender = p_sender;
        }
    }
}
